package org.xx.armory.security;

import org.xx.armory.config.ConfigurationManager;
import org.xx.armory.config.security.KeySection;

import javax.crypto.SecretKey;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static org.xx.armory.commons.KeyUtils.buildDESKey;
import static org.xx.armory.commons.KeyUtils.buildRSAPrivateKey;
import static org.xx.armory.commons.KeyUtils.buildRSAPublicKey;
import static org.xx.armory.commons.Validators.notBlank;

/**
 * 保存密钥的仓库类。
 * <p>该类保存的所有密钥都来自于配置文件</p>
 */
public final class KeyStorage {
    private static final KeyStorage INSTANCE = new KeyStorage();
    private final Map<String, Key> allKeys;

    private KeyStorage() {
        allKeys = new ConcurrentHashMap<>();
    }

    /**
     * 获取密钥仓库类的实例。
     *
     * @return 密钥仓库类的实例。
     */
    public static KeyStorage getInstance() {
        return INSTANCE;
    }

    private byte[] loadKeyBytes(
            String name
    ) {
        final KeySection section = ConfigurationManager.getConfiguration().getSecurity().getKey(name);
        if (section == null) {
            throw new IllegalStateException("cannot find config section <root/security/key[name=" + name + "]>");
        } else {
            return section.getValue();
        }
    }

    private SecretKey loadDESKey(
            String name
    ) {
        try {
            return buildDESKey(loadKeyBytes(name));
        } catch (GeneralSecurityException ex) {
            throw new IllegalStateException("cannot load DES key from <root/security/key[name=" + name + "]>");
        }
    }

    private PublicKey loadRSAPublicKey(
            String name
    ) {
        try {
            return buildRSAPublicKey(loadKeyBytes(name));
        } catch (GeneralSecurityException ex) {
            throw new IllegalStateException("cannot load RSA public key from <root/security/key[name=" + name + "]>");
        }
    }

    private PrivateKey loadRSAPrivateKey(
            String name
    ) {
        try {
            return buildRSAPrivateKey(loadKeyBytes(name));
        } catch (GeneralSecurityException ex) {
            throw new IllegalStateException("cannot load RSA private key from <root/security/key[name=" + name + "]>");
        }
    }

    /**
     * 从配置文件中获取指定名字的DES密钥。
     *
     * @param name
     *         密钥的名字，自动去掉首尾空格。
     * @return 具有指定名字的DES密钥。
     * @throws IllegalArgumentException
     *         如果参数{@code name}是{@code null}或者只包含空白字符。
     * @throws IllegalStateException
     *         如果指定名字的密钥不存在或者无法被转化为DES密钥。
     */
    public SecretKey getDESKey(
            String name
    ) {
        name = notBlank(name, "name").trim();

        return SecretKey.class.cast(this.allKeys.computeIfAbsent(name, this::loadDESKey));
    }

    /**
     * 从配置文件中获取指定名字的RSA公钥。
     *
     * @param name
     *         密钥的名字，自动去掉首尾空格。
     * @return 具有指定名字的RSA公钥。
     * @throws IllegalArgumentException
     *         如果参数{@code name}是{@code null}或者只包含空白字符。
     * @throws IllegalStateException
     *         如果指定名字的密钥不存在或者无法被转化为RSA公钥。
     */
    public PublicKey getRSAPublicKey(
            String name
    ) {
        name = notBlank(name, "name").trim();

        return PublicKey.class.cast(this.allKeys.computeIfAbsent(name, this::loadRSAPublicKey));
    }

    /**
     * 从配置文件中获取指定名字的RSA私钥。
     *
     * @param name
     *         密钥的名字，自动去掉首尾空格。
     * @return 具有指定名字的RSA公钥。
     * @throws IllegalArgumentException
     *         如果参数{@code name}是{@code null}或者只包含空白字符。
     * @throws IllegalStateException
     *         如果指定名字的密钥不存在或者无法被转化为RSA私钥。
     */
    public PrivateKey getRSAPrivateKey(
            String name
    ) {
        name = notBlank(name, "name").trim();

        return PrivateKey.class.cast(this.allKeys.computeIfAbsent(name, this::loadRSAPrivateKey));
    }
}
